#include <AST/expr/function/CallAST.hpp>
#include <AST/expr/var/VarAST.hpp>
#include <global.hpp>
#include <algorithm>
#include <iostream>
#include <AST/expr/literal/IntAST.hpp>
#include <AST/expr/function/PrototypeAST.hpp>

Call::Call(string funName, ArgsVector args)
    : funName(std::move(funName)), args(std::move(args)) {
    // TODO: 计算函数的返回值类型
    // using namespace std;
    // cerr << "call: " << this->funName << " args num:" << this->args.size() <<
    // endl;
}

llvm::Value *Call::codegen() {
    llvm::Function *calleeF = theModule.getFunction(funName);

    if (calleeF == nullptr)
        return logError("Call::codegen: Unknown function name: " + funName);

    const PrototypeAST *const proto = theScope.findProto(funName);

    // TODO: 检查参数列表
    /*
    if (!std::equal(
            args.begin(), args.end(), calleeF->arg_begin(),
            calleeF->arg_end())) {};
    */

    using namespace std;
    cerr << calleeF->arg_size() << " " << args.size() << endl;
    if (!calleeF->isVarArg() && calleeF->arg_size() != args.size())
        return logError(
            "Call::codegen: number of args mismatch in " + funName);

    llvm::SmallVector<Value *> argsV;

    for (unsigned int i = 0; i < args.size(); ++i) {
        Value *v = nullptr;
        // 情况：如若参数变长，则默认对变量按引用传递，对常量按值传递
        if (i >= calleeF->arg_size()) {
            if (Var *var = dynamic_cast<Var *>(args[i]);
                var != nullptr && funName != "printf"
                && funName != "puts") // FIXME: magic
            {
                v = var->refCodegen(); // 按引用，传指针
            } else {
                v = args[i]->codegen(); // 按值
            }
            if (v == nullptr) return nullptr;
        } else {
            // 参数不变长
            llvm::Argument *const para = calleeF->getArg(i);
            // 语法糖：引用切换为指针
            if (proto && proto->getParaIsRef(i)) // 确认是引用关系
            {
                Var *var = dynamic_cast<Var *>(args[i]);
                if (var == nullptr)
                    return logError(
                        "Call::codegen: Expect an Var for ref, but literal found"
                        " in " + funName);
                v = var->refCodegen(); // 确认是引用，传递指针
            } else {
                v = args[i]->codegen(); // 不是引用，不自动取地址
            }
            if (v == nullptr) return nullptr;
            Type *type0 = v->getType(), *type1 = para->getType();
            // 自动对数组解一次引用
            // 或 有长度数组转为无长度数组
            if (type0 != type1
                && (BasicType::isPtrElementEqualAsPtr(type0, type1)
                    || BasicType::isPtrArraySizeDifferentOnly(type0, type1))) {
                v = builder.CreateBitOrPointerCast(v, type1);
                type0 = v->getType();
            }
            if (type0 != type1
                && !BasicType::isPtrElementEqualAsPtr(type0, type1)
                && !BasicType::isPtrArraySizeDifferentOnly(type0, type1)) {
                print_t(type0);
                print_t(type1);
                return logError(
                    "Call::codegen: type mismatch at #" + std::to_string(i)
                    + " in " + funName);
            }
        }

        argsV.push_back(v);
    }

    return builder.CreateCall(
        calleeF, argsV,
        calleeF->getReturnType() != BasicType::VoidTy ? "tmpCall" : "");
}